#include "AsyncLogging.h"

namespace Logger
{
    AsyncLogging::AsyncLogging(const std::string &baseName, off_t rollSize, int flushInterval)
        : baseName_(baseName),
          rollSize_(rollSize),
          flushInterval_(flushInterval),
          currBuffer_(new LargeBuffer),
          nextBuffer_(new LargeBuffer),
          thread_(std::bind(&AsyncLogging::threadFunc, this), "AsyncLogging"),
          isRunning_(false)
    {
        this->buffers_.reserve(16); // 只维持队列长度2~16.
    }

    void AsyncLogging::append(const char *logLine, int len)
    {
        // 加锁
        std::lock_guard<std::mutex> lock(this->mtx_);

        if (this->currBuffer_->avail() > static_cast<size_t>(len)) // 足够写入
        {
            this->currBuffer_->append(logLine, len); // 前端写curBuf
        }
        else
        {
            // curBuf写满就std::move(curBuf)给缓冲区指针队列
            this->buffers_.push_back(std::move(this->currBuffer_));

            // std::move(nxtBuf)给cuBuf 或 清空
            if (this->nextBuffer_)
            {
                this->currBuffer_ = std::move(nextBuffer_);
            }
            else
            {
                this->currBuffer_.reset(new LargeBuffer);
            }
            this->currBuffer_->append(logLine, len);

            this->cond_.notify_one(); // 唤醒线程刷磁盘
        }
    }

    void AsyncLogging::threadFunc()
    {
        LogFile logFile(this->baseName_, this->rollSize_);

        // 线程创建缓冲区指针队列+两个缓冲区
        BufferPtr newCurrBuffer(new LargeBuffer);
        BufferPtr newNextBuffer(new LargeBuffer);
        BufferVector newBuffers{};
        // 注意：
        // reserve()：影响capacity，<16避免扩缩容
        // resize()：影响size
        // swap()：无论size都交换所有
        newBuffers.reserve(16);

        while (this->isRunning_)
        {
            {
                // 条件变量加互斥锁
                std::unique_lock<std::mutex> lock(this->mtx_);
                if (this->buffers_.empty())
                {
                    this->cond_.wait_for(lock, std::chrono::seconds(3));
                }

                // std::move()两个缓冲区（move()swap()高效减小锁粒度）
                this->buffers_.push_back(std::move(this->currBuffer_)); // 注意要把当前端正在写的也一起处理
                this->currBuffer_ = std::move(newCurrBuffer);
                if (!this->nextBuffer_)
                {
                    this->nextBuffer_ = std::move(newNextBuffer);
                }
                // swap()缓冲区指针队列
                newBuffers.swap(this->buffers_);
            } // 解锁

            // 刷磁盘
            for (auto &buffer : newBuffers)
            {
                logFile.append(buffer->getData(), buffer->getLen());
            }

            // 回收缓冲区指针队列节省内存，为2见后面逻辑
            if (newBuffers.size() > 2)
            {
                newBuffers.resize(2);
            }
            // 回收两个缓冲区
            if (!newCurrBuffer)
            {
                newCurrBuffer = std::move(newBuffers.back());
                newBuffers.pop_back();
                newCurrBuffer->reset();
            }
            if (!newNextBuffer)
            {
                newNextBuffer = std::move(newBuffers.back());
                newBuffers.pop_back();
                newNextBuffer->reset();
            }

            newBuffers.clear(); // 清空
            logFile.flush();    // 刷盘
        }
        logFile.flush(); // 退出运行时再刷一次避免丢数据
    }
} // namespace Logger